import { ChangeDetectorRef, Component, Input, TemplateRef } from '@angular/core';
import { ComponentFixture, TestBed, tick, fakeAsync, waitForAsync } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TooltipModule } from './tooltip.module';
import { ButtonModule } from '../button/api';
import { ChangeDetectionStrategy } from '@angular/compiler';
import { InputModule } from '../input/input.module';

const POSITIONS = ['top', 'topLeft', 'topRight', 'right', 'rightTop', 'rightBottom'];

@Component({
  template: `
    <button
      lv-button
      [lv-tooltip]="lvContent"
      [lvTooltipIcon]="lvIcon"
      [lvTooltipTheme]="lvTheme"
      [lvTooltipTrigger]="lvTrigger"
      [lvTooltipPosition]="lvPosition"
      [(lvTooltipVisible)]="lvVisible"
      [lvTooltipClassName]="lvClassName"
      [lvTooltipShowArrow]="lvShowArrow"
      (click)="toggle()"
    >
      Trigger
    </button>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestTooltipComponent {
  @Input() lvContent: string | TemplateRef<void> = null;
  @Input() lvIcon: string = '';
  @Input() lvTheme: 'light' | 'dark' = 'light';
  @Input() lvPosition:
    | 'top'
    | 'topLeft'
    | 'topRight'
    | 'right'
    | 'rightTop'
    | 'rightBottom'
    | 'bottom'
    | 'bottomLeft'
    | 'bottomRight'
    | 'left'
    | 'leftTop'
    | 'leftBottom' = 'top';
  @Input() lvTrigger: 'focus' | 'hover' | 'click' | 'customize' = 'hover';
  @Input() lvClassName: string = '';
  @Input() lvShowArrow: boolean = true;

  lvVisible: boolean = false;

  toggle(): void {
    if (this.lvTrigger == 'customize') {
      this.lvVisible = !this.lvVisible;
    }
  }
}

describe('TooltipDirective', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      imports: [TooltipModule, ButtonModule, NoopAnimationsModule],
      declarations: [TestTooltipComponent],
      teardown: { destroyAfterEach: true },
    }).compileComponents;
  }));

  let component: TestTooltipComponent;
  let fixture: ComponentFixture<TestTooltipComponent>;
  let cdr: ChangeDetectorRef;

  const getButton = () => fixture.nativeElement.querySelector('.lv-button');
  const getTooltip = () => document.querySelector('.lv-tooltip');

  const detectChanges = (isCdr = false) => {
    tick(500);
    if (isCdr) {
      cdr.detectChanges();
    } else {
      fixture.detectChanges();
    }
  };

  describe('render', () => {
    const renderComponent = params => {
      for (const [key, value] of Object.entries(params)) {
        component[key] = value;
      }
      cdr.detectChanges();
      getButton().dispatchEvent(new Event('mouseenter'));
      detectChanges();
      expect(getTooltip()).toMatchSnapshot();
      getButton().dispatchEvent(new Event('mouseleave'));
      detectChanges();
    };

    beforeEach(() => {
      fixture = TestBed.createComponent(TestTooltipComponent);
      component = fixture.componentInstance;
      cdr = fixture.debugElement.injector.get(ChangeDetectorRef);
      component.lvContent = 'This is a Tooltip';
    });

    it('with lvIcon', fakeAsync(() => {
      renderComponent({ lvIcon: 'icon-account' });
    }));

    it('with lvTheme', fakeAsync(() => {
      renderComponent({ lvTheme: 'dark' });
    }));

    it('with lvPosition', fakeAsync(() => {
      POSITIONS.slice(1).forEach(key => {
        renderComponent({ lvPosition: key });
      });
    }));

    it('with lvShowArrow', fakeAsync(() => {
      renderComponent({ lvShowArrow: false });
    }));
  });
  describe('work', () => {
    const renderComponent = param => {
      for (const [key, value] of Object.entries(param)) {
        component[key] = value;
      }
      fixture.detectChanges();
    };

    const getTooltipText = () => document.querySelector('.lv-tooltip-container').textContent;

    beforeEach(() => {
      fixture = TestBed.createComponent(TestTooltipComponent);
      component = fixture.componentInstance;
      cdr = fixture.debugElement.injector.get(ChangeDetectorRef);
      component.lvContent = 'This is a Tooltip';
    });

    it('with lvTrigger is click', fakeAsync(() => {
      renderComponent({ lvTrigger: 'click' });
      const button = getButton();
      button.dispatchEvent(new Event('click'));
      detectChanges();
      expect(getTooltip()).toBeTruthy();
      expect(getTooltipText()).toBe(component.lvContent);

      button.dispatchEvent(new Event('click'));
      detectChanges();
      detectChanges();
      expect(getTooltip()).toBeNull();
    }));

    it('with lvTrigger is customize', fakeAsync(() => {
      renderComponent({ lvTrigger: 'customize' });

      document.dispatchEvent(new Event('mouseover'));
      detectChanges();

      const button = getButton();
      button.dispatchEvent(new Event('click'));
      detectChanges();
      detectChanges();
      expect(getTooltip()).toBeTruthy();

      button.dispatchEvent(new Event('click'));
      detectChanges();
      detectChanges();
      detectChanges();
      expect(getTooltip()).toBeNull();
    }));

    it('with lvClassName', fakeAsync(() => {
      const className = 'aui-custom-tooltip';
      renderComponent({ lvClassName: className });

      const button = getButton();
      button.dispatchEvent(new Event('mouseenter'));
      detectChanges();
      expect(getTooltip().className).toContain(className);
    }));
  });
});

@Component({
  template: `
    <input
      lv-input
      style="width: 368px"
      lv-tooltip="Focus me"
      lvTooltipTrigger="focus"
      lvTooltipPosition="right"
      lvTooltipIcon="lv-icon-status-failed"
    />
  `,
})
class TestTooltipFocusComponent {}

describe('TooltipDirective', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      imports: [TooltipModule, InputModule, NoopAnimationsModule],
      declarations: [TestTooltipFocusComponent],
      teardown: { destroyAfterEach: true },
    }).compileComponents;
  }));

  let fixture: ComponentFixture<TestTooltipFocusComponent>;

  const detectChanges = () => {
    tick(500);
    fixture.detectChanges();
  };

  const getTooltip = () => document.querySelector('.lv-tooltip');

  describe('work', () => {
    it('with lvTrigger is focus', fakeAsync(() => {
      fixture = TestBed.createComponent(TestTooltipFocusComponent);
      fixture.detectChanges();

      const input = fixture.nativeElement.querySelector('.lv-input');
      input.focus();
      detectChanges();
      expect(getTooltip()).toBeTruthy();

      input.blur();
      detectChanges();
      detectChanges();
      expect(getTooltip()).toBeNull();
    }));
  });
});
